/*
 * Copyright (c) 2019-2029, <a href="https://wwww.intellijboy.cn">https://wwww.intellijboy.cn</a> (liuburu@qq.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * <a href="https://www.apache.org/licenses/LICENSE-2.0">https://www.apache.org/licenses/LICENSE-2.0</a>
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package cn.intellijboy.gradle.plugin.task.render;

import cn.hutool.core.collection.CollectionUtil;
import cn.intellijboy.gradle.plugin.model.DependencyModel;
import lombok.Getter;
import org.gradle.api.artifacts.ModuleIdentifier;
import org.gradle.api.artifacts.component.ComponentIdentifier;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableDependency;
import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableDependencyResult;
import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableModuleResult;

import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

/**
 * 星哲依赖图渲染器
 *
 * @author intellijboy
 * @since 1.0
 */
public class XingzheDependencyGraphsRenderer {

    @Getter
    private final Set<DependencyModel> collectDependencies;

    @Getter
    private final Set<String> visitedDependencyIds;

    public XingzheDependencyGraphsRenderer() {
        this.collectDependencies = new HashSet<>();
        visitedDependencyIds = new HashSet<>();
    }

    /**
     * 渲染依赖关系图
     *
     * @param root 根依赖节点，作为渲染的起始点
     */
    public void render(RenderableDependency root) {
        // 创建已访问节点集合，用于避免循环依赖导致的重复渲染
        // 递归遍历解析
        recursiveRenderNode(root, null);
    }

    /**
     * 递归渲染依赖节点及其子节点
     * 该方法通过深度优先遍历的方式渲染依赖树中的所有节点，使用visited集合避免重复渲染相同的节点
     *
     * @param current 当前节点
     * @param parent  父节点
     */
    private void recursiveRenderNode(RenderableDependency current, RenderableDependency parent) {
        // 执行渲染业务逻辑
        doRenderAction(current, parent);
        if (getVisitedDependencyIds().add(current.getId().toString())) {
            // 当前节点第一次访问
            Set<? extends RenderableDependency> children = current.getChildren();
            if (CollectionUtil.isNotEmpty(children)) {
                for (RenderableDependency child : children) {
                    recursiveRenderNode(child, current);
                }
            }
        }
    }

    private void doRenderAction(RenderableDependency current, RenderableDependency parent) {
        Optional.ofNullable(convertToDependencyModel(current, parent))
                .ifPresent(model -> this.getCollectDependencies().add(model));
    }

    private DependencyModel convertToDependencyModel(RenderableDependency current, RenderableDependency parent) {
        if (current instanceof RenderableDependencyResult renderableDependencyResult) {
            ComponentIdentifier identifier = renderableDependencyResult.getId();
            if (identifier instanceof ModuleComponentIdentifier moduleComponentIdentifier) {
                ModuleIdentifier moduleIdentifier = moduleComponentIdentifier.getModuleIdentifier();
                String parentId = getParentId(parent);
                return DependencyModel.builder()
                        .id(current.getId().toString())
                        .parentId(parentId)
                        .group(moduleIdentifier.getGroup())
                        .group(moduleIdentifier.getGroup())
                        .name(moduleIdentifier.getName())
                        .version(moduleComponentIdentifier.getVersion())
                        .build();
            }
        } else if (current instanceof RenderableModuleResult renderableModuleResult) {
            return DependencyModel.builder()
                    .id(current.getId().toString())
                    .name(renderableModuleResult.getName())
                    .build();
        }
        return null;
    }

    private String getParentId(RenderableDependency parent) {
        if (parent == null) {
            return null;
        }
        if (parent instanceof RenderableModuleResult) {
            return null;
        }
        String parentId = parent.getId().toString();
        if (parentId.startsWith("project :")) {
            return null;
        }
        return parentId;
    }

}
